Skip to content

fix: record my own Discord profile at READY so a sending message keeps my avatar - #53

Merged
itz4blitz merged 1 commit into
mainfrom
fix/pending-message-avatar
Oct 20, 2026
Merged

itz4blitz merged 1 commit into
mainfrom
fix/pending-message-avatar

Conversation

@itz4blitz

@itz4blitz itz4blitz commented Oct 18, 2026 •

Copy link
Copy Markdown
Owner

Cause

A message being sent is built by server.rs::new_message with no sender id or avatar. For an outgoing message with no avatar, gui/conversation/draw.rs::avatar used the account's picture (AccountStatus.avatar). Store::accounts reads that picture from the person row whose id is the account's me, joined to that avatar's media row. On Discord that row was never written. READY called only ctx.describe(name, me) (discord/ready.rs), and so did USER_UPDATE (discord/events.rs), so the account picture was nearly always None and the sending row showed the generic plate. The echo carries sender.avatar (avatar:{user}:{hash}, the global picture, from discord/content.rs), so the real picture appeared once Discord answered.

This PR used to say the earlier messages carry "a server-specific picture of me". That was wrong. chatd never produces one: Discord's Member has no avatar field, and messages always carry the global avatar.

Fix

chatd (adapters/discord/ready.rs, events.rs)

  • New Discord::myself(user): registers my avatar media (ctx.media), records my own person row (ctx.people(&[user.person(None)])), then calls ctx.describe. This is the same User::avatar / User::person path used for relationships, DM recipients and member chunks. The row is written before describe, so the Change::Status it emits already has the picture.
  • READY calls myself(&ready.user) instead of describe.
  • USER_UPDATE calls it only when the user id is mine. Before, any USER_UPDATE overwrote the account's name and me.
  • Because the account picture now resolves, the rail shows my Discord picture, and chatd's existing own-picture fetch (blobs::own_pictures) downloads it from the CDN once per connect when it is not already on disk. Discord never triggered that fetch before.

GUI (gui/conversation/draw.rs)

  • own_face now picks, for any message of mine: the picture the message names, then the account picture, then (last resort) the picture on my newest message in the window. Stored messages keep the avatar id they were stored with, so a history picture no longer beats a newer account picture after I change mine.
  • The history fallback stays for networks that keep no row for me. For example, Teams' lookups skip my own id, while my messages carry avatar:8:…. An empty me skips the scan.
  • said_by (the history search who already used) is unchanged from the earlier revision. Doc comments no longer mention a server-specific picture.

Where I can now show up. My row has relation none, so it is not in the People tab, the friend/request/blocked lists, owned, incoming, or talked_to (I am never a DM peer). DM and group titles come from the recipients, not the person table, so they don't change. Member lists come from Discord's lists, which already include me. A people search or @ completion with a query can now match me from the cache. That is already true on Telegram and Slack, which record my row, and on Discord, whose member search already recorded me when it matched. An @ with no query still lists only owned people.

Validation

  • New my_picture_is_the_accounts_from_ready_and_follows_a_change_to_it in crates/chatd/tests/discord.rs checks that:

    • after READY the account status carries avatar:1000:me0hash, and chatd fetches it with nothing asked by the shell
    • I am in none of the owned lists (all, friends, blocked)
    • a USER_UPDATE of mine with a new hash becomes the account picture
    • another user's USER_UPDATE changes neither the account name nor its picture

    With the chatd change reverted, it fails with left: ("Justin", None).

  • The fake Discord counts fetches of my own picture apart from requests, because chatd fetches it on its own 2 s clock. Without that, three tests that assert exact request lists or counts failed. The large-READY test now also asserts the picture is fetched exactly once in the first 30 s.

  • GUI tests that replace the old circular ones:

    • order: own → account → history
    • a picture I have since changed does not come back from history when the account has the new one
    • history lends only my newest picture (a tombstone does not lose it), never someone else's, and nothing without me
  • cargo test -p chatd --test discord: 32 passed, 0 failed

  • cargo test -p chatd: 292 passed, 0 failed (all targets)

  • cargo test -p tinyclient --features gui: 351 passed, 0 failed, 1 ignored

  • cargo clippy -p chatd -p tinyclient --features gui --all-targets: on this branch it stops at a deny-level clippy::while_immutable_condition in discord/gateway.rs:488. That file is untouched here and identical on main. With that one lint allowed, the run finishes with nothing in the touched files.

Not checked

  • Not run in the live app against a real Discord account, so the pending-row flicker and the rail picture were not seen.
  • Teams appears to record no row for me either (its lookups skip my id; read from the code, not run), so its account picture would stay empty and rely on the GUI's history fallback. Left for a separate change.

Fixes #46

🤖 Generated with Claude Code

@itz4blitz

Copy link
Copy Markdown
Owner Author

@vbmithr this one fixes the avatar flicker from #46.

What changed (crates/tinyclient/src/gui/conversation/draw.rs, GUI only, no request per send):

  • Shared history lookup. The search that who already did is now a small function, said_by, which who reuses with unchanged behaviour.

  • New own_face for your own messages. It picks, in order:

    1. the message's own avatar;
    2. otherwise the avatar on your newest earlier message in this conversation, matched on your account id;
    3. otherwise the account avatar.

    Step 2 is the server-specific picture when Discord sent one. So the pending row now shows the same picture the confirmed echo carries, and nothing changes when it lands.

Tests (gui::conversation::draw::tests):

  • A pending message with no avatar, plus an account snapshot with no avatar, plus earlier own messages with an avatar, keeps that avatar. A server-specific avatar wins over the global one, and a newer own message with no picture doesn't lose it.
  • With no own messages in view, it falls back to the account avatar, or to none.
  • The picture is the same before and after the confirmed message replaces the pending one.

Not covered:

  • If none of your messages are in the loaded part of the conversation, it still uses the account avatar.
  • I didn't look into image-cache readiness, the second cause you raised.

This branch also carries the fix for a flaky macOS pacing test (#54), which was failing CI here.

To try it:

git fetch origin pull/53/head:pr-53 && git checkout pr-53
cargo test -p tinyclient --features gui gui::conversation::draw
cargo run -p tinyclient --features gui                     # starts chatd beside it

In a Discord channel where your earlier messages show your avatar (ideally a server where you have a per-server avatar), send a message. The sending row should show the same avatar from start to finish, with no generic icon.

…s my avatar

A message being sent names no picture, so its row shows the account's. On Discord that was missing: the account's picture is read from my own row in People, and READY and USER_UPDATE only set the account's name. READY now registers my picture and records my row before naming the account, and a USER_UPDATE of mine does the same, so the picture is there from connect and follows a change. The rail and chatd's own-picture fetch get it too.

For a message of mine the window now shows its own picture, then the account's, and only then the one my newest message here was stored with, for a network that keeps no row of me. A stored picture is never re-read, so it no longer beats a newer account picture.

The fake Discord counts fetches of my picture apart from what is asked of the API, since chatd fetches it on its own clock.

Fixes #46

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@itz4blitz
itz4blitz force-pushed the fix/pending-message-avatar branch 2 times, most recently from 7f6a9e5 to c2d4662 Compare October 20, 2026 13:37
@itz4blitz itz4blitz changed the title fix: keep my avatar from earlier messages on a Discord message being sent fix: record my own Discord profile at READY so a sending message keeps my avatar Oct 20, 2026
@vbmithr

vbmithr commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Doesn’t seem to work here.

@itz4blitz
itz4blitz merged commit 3558d8d into main Oct 20, 2026
11 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Discord avatar briefly becomes generic while an outgoing message is pending

2 participants